package net.xdclass.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.lock.LockInfo;
import com.baomidou.lock.LockTemplate;
import com.baomidou.lock.executor.RedissonLockExecutor;
import lombok.extern.slf4j.Slf4j;
import net.xdclass.component.ShortLinkComponent;
import net.xdclass.config.RabbitMQConfig;
import net.xdclass.config.RabbitMQDelConfig;
import net.xdclass.config.RabbitMQUpdateConfig;
import net.xdclass.controller.request.ShortLinkAddRequest;
import net.xdclass.controller.request.ShortLinkDelRequest;
import net.xdclass.controller.request.ShortLinkPageRequest;
import net.xdclass.controller.request.ShortLinkUpdateRequest;
import net.xdclass.enums.BizCodeEnum;
import net.xdclass.enums.DomainTypeEnum;
import net.xdclass.enums.EventMessageType;
import net.xdclass.enums.ShortLinkStateEnum;
import net.xdclass.exception.BizException;
import net.xdclass.interceptor.LoginInterceptor;
import net.xdclass.manager.GroupCodeMappingManager;
import net.xdclass.manager.LinkGroupManager;
import net.xdclass.manager.ShortLinkManager;
import net.xdclass.model.*;
import net.xdclass.service.DomainService;
import net.xdclass.service.ShortLinkService;
import net.xdclass.util.CommonUtil;
import net.xdclass.util.IDUtil;
import net.xdclass.util.JsonData;
import net.xdclass.util.JsonUtil;
import net.xdclass.vo.ShortLinkVO;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.util.Map;

/**
 * 小滴课堂,愿景：让技术不再难学
 *
 * @Description
 * @Author 二当家小D
 * @Remark 有问题直接联系我，源码-笔记-技术交流群
 * @Version 1.0
 **/

@Service
@Slf4j
public class ShortLinkServiceImpl implements ShortLinkService {

	@Resource
	private ShortLinkManager shortLinkManager;

	@Autowired
	private RabbitTemplate rabbitTemplate;

	@Autowired
	private RabbitMQConfig rabbitMQConfig;

	@Resource
	private RabbitMQDelConfig rabbitMQDelConfig;

	@Resource
	private RabbitMQUpdateConfig rabbitMQUpdateConfig;


	@Resource
	private DomainService domainService;


	@Resource
	private ShortLinkComponent shortLinkComponent;


	@Resource
	private LinkGroupManager linkGroupManager;


	@Resource
	private GroupCodeMappingManager groupCodeMappingManager;




	@Resource
	private LockTemplate lockTemplate;



	@Override
	public ShortLinkVO parseShortLinkCode(String shortLinkCode) {

		ShortLinkDO shortLinkDO = shortLinkManager.findByShortLinCode(shortLinkCode);
		if (shortLinkDO == null) {
			return null;
		}
		ShortLinkVO shortLinkVO = new ShortLinkVO();
		BeanUtils.copyProperties(shortLinkDO, shortLinkVO);
		return shortLinkVO;
	}

	@Override
	public JsonData createShortLink(ShortLinkAddRequest request) {

		Long accountNo = LoginInterceptor.threadLocal.get().getAccountNo();

		String newOriginalUrl = CommonUtil.addUrlPrefix(request.getOriginalUrl());

		request.setOriginalUrl(newOriginalUrl);
		EventMessage eventMessage = EventMessage.builder().accountNo(accountNo)
			.content(JsonUtil.obj2Json(request))
			.messageId(IDUtil.genSnowFlakeID().toString())
			.eventMessageType(EventMessageType.SHORT_LINK_ADD.name())
			.build();

		rabbitTemplate.convertAndSend(rabbitMQConfig.getShortLinkEventExchange(), rabbitMQConfig.getShortLinkAddRoutingKey(), eventMessage);

		return JsonData.buildSuccess();
	}

	/**
	 * 处理新增短链消息
	 *
	 * @param eventMessage
	 * @return
	 */
	@Override
	public boolean handlerAddShortLink(EventMessage eventMessage) {
		//判断短链域名是否合法
		//判断组名是否合法
		//⽣成⻓链摘要
		//⽣成短链码
		//加锁
		//查询短链码是否存在
		//构建短链对象
		//保存数据库
		Long accountNo = eventMessage.getAccountNo();
		String messageType = eventMessage.getEventMessageType();

		ShortLinkAddRequest addRequest = JsonUtil.json2Obj(eventMessage.getContent(), ShortLinkAddRequest.class);
		//短链域名校验
		DomainDO domainDO = checkDomain(addRequest.getDomainType(), addRequest.getDomainId(), accountNo);
		//校验组是否合法
		LinkGroupDO linkGroupDO = checkLinkGroup(addRequest.getGroupId(), accountNo);

		//长链摘要
		String originalUrlDigest = CommonUtil.MD5(addRequest.getOriginalUrl());

		//生成短链码
		String shortLinkCode = shortLinkComponent.createShortLinkCode(addRequest.getOriginalUrl());

		log.info("短链码为：{},它属于线程{}", shortLinkCode, Thread.currentThread().getId());
		Boolean isDuplicate = Boolean.FALSE;


		LockInfo lockInfo = lockTemplate.lock(shortLinkCode, 30000L, 5000L, RedissonLockExecutor.class);

		log.info("线程:{}获取到了锁：{}", Thread.currentThread().getId(), lockInfo.toString());
		if (null == lockInfo) {
			throw new BizException(BizCodeEnum.OPS_REPEAT);
		}
		try {


			//C端处理
			if (EventMessageType.SHORT_LINK_ADD_LINK.name().equalsIgnoreCase(messageType)) {

				//先判断是否短链码被占用
				ShortLinkDO shortLinCodeDOInDB = shortLinkManager.findByShortLinCode(shortLinkCode);

				if (ObjectUtil.isNull(shortLinCodeDOInDB)) {
					ShortLinkDO shortLinkDO = ShortLinkDO.builder()
						.accountNo(accountNo)
						.code(shortLinkCode)
						.title(addRequest.getTitle())
						.originalUrl(addRequest.getOriginalUrl())
						.domain(domainDO.getValue())
						.groupId(linkGroupDO.getId())
						.expired(addRequest.getExpired())
						.sign(originalUrlDigest)
						.state(ShortLinkStateEnum.ACTIVE.name())
						.del(0)
						.build();
					shortLinkManager.addShortLink(shortLinkDO);
					return true;
				} else {
					log.warn("C端短链码重复：{}", shortLinCodeDOInDB.getCode());
					isDuplicate = Boolean.TRUE;
				}


			} else if (EventMessageType.SHORT_LINK_ADD_MAPPING.name().equalsIgnoreCase(messageType)) {

				//先判断是否短链码被占用
				ShortLinkDO shortLinCodeDOInDB = shortLinkManager.findByShortLinCode(shortLinkCode);
				if (ObjectUtil.isNull(shortLinCodeDOInDB)) {
					//B端处理
					GroupCodeMappingDO groupCodeMappingDO = GroupCodeMappingDO.builder()
						.accountNo(accountNo)
						.code(shortLinkCode)
						.title(addRequest.getTitle())
						.originalUrl(addRequest.getOriginalUrl())
						.domain(domainDO.getValue())
						.groupId(linkGroupDO.getId())
						.expired(addRequest.getExpired())
						.sign(originalUrlDigest)
						.state(ShortLinkStateEnum.ACTIVE.name())
						.del(0)
						.build();

					groupCodeMappingManager.add(groupCodeMappingDO);
					return true;
				} else {
					log.warn("B端短链码重复：{}", shortLinCodeDOInDB.getCode());
					isDuplicate = Boolean.TRUE;
				}
			}


//			if (isDuplicate) {
//				log.info("短链码需重新生成");
//			EventMessage eventMessage1 = new EventMessage();
//			ShortLinkAddRequest shortLinkAddRequest1 = new ShortLinkAddRequest();
//			BeanUtils.copyProperties(eventMessage,eventMessage1);
//			BeanUtils.copyProperties(addRequest,shortLinkAddRequest1);
//			String newUrl = CommonUtil.addUrlPrefixVersion(addRequest.getOriginalUrl());
//
//			shortLinkAddRequest1.setOriginalUrl(newUrl);
//
//			String content = JsonUtil.obj2Json(addRequest);
//			eventMessage1.setContent(content);
//			handlerAddShortLink(eventMessage1);

//
//				String newOriginalUrl = CommonUtil.addUrlPrefixVersion(addRequest.getOriginalUrl());
//				addRequest.setOriginalUrl(newOriginalUrl);
//				eventMessage.setContent(JsonUtil.obj2Json(addRequest));
//				log.warn("短链码报错失败，重新生成:{}", eventMessage);
//				handlerAddShortLink(eventMessage);
//			}
		} finally {
			lockTemplate.releaseLock(lockInfo);
		}
		return false;
	}

	@Override
	public Map<String, Object> pageByGroupId(ShortLinkPageRequest request) {
		Long accountNo = LoginInterceptor.threadLocal.get().getAccountNo();

		Map<String, Object> result = groupCodeMappingManager.pageShortLinkByGroupId(request.getPage(), request.getSize(), accountNo, request.getGroupId());

		return result;
	}

	@Override
	public JsonData del(ShortLinkDelRequest request) {
		Long accountNo = LoginInterceptor.threadLocal.get().getAccountNo();

		EventMessage eventMessage = EventMessage.builder().accountNo(accountNo)
			.content(JsonUtil.obj2Json(request))
			.messageId(IDUtil.genSnowFlakeID().toString())
			.eventMessageType(EventMessageType.SHORT_LINK_DEL.name())
			.build();

		rabbitTemplate.convertAndSend(rabbitMQConfig.getShortLinkEventExchange(), rabbitMQDelConfig.getShortLinkDelRoutingKey(), eventMessage);


		return JsonData.buildSuccess();
	}

	@Override
	public JsonData update(ShortLinkUpdateRequest request) {
		Long accountNo = LoginInterceptor.threadLocal.get().getAccountNo();

		EventMessage eventMessage = EventMessage.builder().accountNo(accountNo)
			.content(JsonUtil.obj2Json(request))
			.messageId(IDUtil.genSnowFlakeID().toString())
			.eventMessageType(EventMessageType.SHORT_LINK_ADD.name())
			.build();

		rabbitTemplate.convertAndSend(rabbitMQConfig.getShortLinkEventExchange(), rabbitMQUpdateConfig.getShortLinkUpdateRoutingKey(), eventMessage);

		return JsonData.buildSuccess();
	}

	@Override
	public boolean handlerDelShortLink(EventMessage eventMessage) {


		Long accountNo = eventMessage.getAccountNo();
		String content = eventMessage.getContent();
		ShortLinkDelRequest shortLinkDelRequest = JsonUtil.json2Obj(content, ShortLinkDelRequest.class);
		String shortLinkDelRequestCode = shortLinkDelRequest.getCode();
		LockInfo lockInfo = lockTemplate.lock(shortLinkDelRequestCode, 30000L, 5000L, RedissonLockExecutor.class);

		log.info("线程:{}获取到了锁：{}", Thread.currentThread().getId(), lockInfo.toString());
		if (null == lockInfo) {
			throw new BizException(BizCodeEnum.OPS_REPEAT);
		}
		try {
			if (eventMessage.getEventMessageType().equals(EventMessageType.SHORT_LINK_DEL_LINK.name())) {

				shortLinkManager.del(shortLinkDelRequestCode, accountNo);
				return Boolean.TRUE;
			} else if (eventMessage.getEventMessageType().equals(EventMessageType.SHORT_LINK_DEL_MAPPING.name())) {
				groupCodeMappingManager.del(shortLinkDelRequestCode, accountNo, shortLinkDelRequest.getGroupId());
				return Boolean.TRUE;

			}


		} finally {
			lockTemplate.releaseLock(lockInfo);
		}

		return Boolean.FALSE;
	}

	/**
	 * 校验域名
	 *
	 * @param domainType
	 * @param domainId
	 * @param accountNo
	 * @return
	 */
	private DomainDO checkDomain(String domainType, Long domainId, Long accountNo) {

		DomainDO domainDO;

		if (DomainTypeEnum.CUSTOM.name().equalsIgnoreCase(domainType)) {
			domainDO = domainService.findById(domainId, accountNo);

		} else {
			domainDO = domainService.findByDomainTypeAndID(domainId, DomainTypeEnum.OFFICIAL);
		}
		Assert.notNull(domainDO, "短链域名不合法");
		return domainDO;
	}

	/**
	 * 校验组名
	 *
	 * @param groupId
	 * @param accountNo
	 * @return
	 */
	private LinkGroupDO checkLinkGroup(Long groupId, Long accountNo) {

		LinkGroupDO linkGroupDO = linkGroupManager.detail(groupId, accountNo);
		Assert.notNull(linkGroupDO, "组名不合法");
		return linkGroupDO;
	}

}
